// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * HiChip HC16xx SPI Flash controller driver
 *
 * Copyright © 2021 HiChip Semiconductor Co., Ltd.
 *              http://www.hichiptech.com
 */
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/spi/spi.h>
#include <linux/io.h>
#include <linux/wait.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinctrl.h>
#include <mtd/mtd-abi.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/version.h>
#include <hc_clk_gate.h>

#if LINUX_VERSION_CODE < KERNEL_VERSION(4,11,0)
#define ALIGN_DOWN(x, a)        __ALIGN_KERNEL((x) - ((a) - 1), (a))
#endif

#define CLK_SEL_REG1 		0x1880007c
#define CLK_SEL_REG1_LEN 	0x20

#define REG_SF_BASE				(0)
#include "sf_reg.h"

#define MAX_INLINE_NUM_CS			(16)
#define SPI_INVALID_DMA_ADDR			(0xffffffff)

#define SFLASH_DMACTRL_TX_DEFCONFIG                                            \
	(F_SF_0064_DMA_INT_EN | F_SF_0064_ARBIT_MODE | F_SF_0064_ACCESS_REQ |  \
	 F_SF_0064_ACCESS_MODE | F_SF_0064_DMA_DIR | F_SF_0064_DMA_START)

#define SFLASH_DMACTRL_RX_DEFCONFIG                                            \
	(F_SF_0064_PRE_READ_EN | F_SF_0064_DMA_INT_EN | F_SF_0064_ARBIT_MODE | \
	 F_SF_0064_ACCESS_REQ | F_SF_0064_ACCESS_MODE | F_SF_0064_DMA_START)

#define SFLASH_CONF0_DEFCONFIG			(0x03)
#define SFLASH_CONF1_DEFCONFIG			(F_SF_0099_DATA_HIT | F_SF_0099_ADDR_HIT | F_SF_0099_CODE_HIT)

/*
 *  mode    cmd    addr+dummy     data
 * MODE0     1          1          1
 * MODE1     1          1          2
 * MODE2     1          2          2
 * MODE3     1          4          4
 * MODE4     4          4          4
 * MODE5     1          1          4
 * MODE6     2          2          2
 */
#define SFLASH_MODE_0 (0)
#define SFLASH_MODE_1 (1)
#define SFLASH_MODE_2 (2)
#define SFLASH_MODE_3 (3)
#define SFLASH_MODE_4 (4)
#define SFLASH_MODE_5 (5)
#define SFLASH_MODE_6 (6)
#define SFLASH_ADDR_3BYTES (0)
#define SFLASH_ADDR_4BYTES (1)
#define SFLASH_ADDR_1BYTES (2)
#define SFLASH_ADDR_2BYTES (3)
#define SFLASH_CONF2_DEFCONFIG			(SFLASH_MODE_0 | SFLASH_ADDR_3BYTES)

#define SFLASH_SIZE_2MB (0)
#define SFLASH_SIZE_4MB (1)
#define SFLASH_SIZE_8MB (2)
#define SFLASH_SIZE_16MB (3)
#define SFLASH_CONF3_DEFCONFIG			(SFLASH_SIZE_16MB << F_SF_009B_SIZE_S)

#define HC_SPI_CLK_108MHZ			(108 * 1000 * 1000)
#define HC_SPI_CLK				(HC_SPI_CLK_108MHZ)

#define DMA_ADDR_ALIGN				(64)
#define DMA_LEN_ALIGN				(64)
#define DMA_LEN_MIN				(0x100)
#define DMA_TRANS_TIMEOUT			msecs_to_jiffies(10000)

struct mutex		slock;
struct hc_spi {
	int			irq;

	u32			sclk;
	u32			dma_mode;

	void __iomem		*iobase;

	void __iomem		*inlinepiobase[MAX_INLINE_NUM_CS];
	void __iomem		**piobase;
	u32			num_cs;

	struct pinctrl		*pctl;
	struct pinctrl_state	*active;

	struct platform_device	*pdev;
	struct spi_device	*spi;

	struct completion	completion;

	struct mutex		lock;
};

enum spi_dma_dir {
	SPI_DMA_TX,
	SPI_DMA_RX
};

extern unsigned int noinline write_sync(void);

static int hc_spi_set_clk_div(struct hc_spi *hs, u32 rate)
{
	int i = 0;
	uint32_t div = 0;
	uint32_t clk_reg = 0;
	uint32_t *sysio_reg;
	uint32_t tmp1 = HC_SPI_CLK, tmp2 = HC_SPI_CLK;

	sysio_reg = ioremap(0x1880007c, 4);

	if (rate > 54 * 1000 * 1000) {
		clk_reg = readl(sysio_reg);
		clk_reg &= 0xffff3fff;
		clk_reg |= 0x0000b000;
		writel(clk_reg, sysio_reg);
		iounmap(sysio_reg);
		return 0;
	}

	for (; i < 0x10; i++) {
		tmp1 = HC_SPI_CLK / (2 * i + 2);
		if (abs(tmp1 - rate) < tmp2) {
			tmp2 = abs(tmp1 - rate);
			div = i;
		}
	}

	clk_reg = readl(sysio_reg);
	clk_reg &= 0xffff3fff;
	writel(clk_reg, sysio_reg);

	iounmap(sysio_reg);
	return div;
}

static void hc_spi_set_clk_rate(struct hc_spi *hs, u32 rate)
{
	u32 div = hc_spi_set_clk_div(hs, rate);

	writeb(SFLASH_CONF3_DEFCONFIG | (div & F_SF_009B_CLK_DIV_M),
	       hs->iobase + R_SF_CONF3);
	writeb((div >> 4) & F_SF_0004_CLK_DIV_EXT_M,
	       hs->iobase + R_SF_CLK_DIV_EXT);
}

static inline void hc_spi_set_clock_mode(struct hc_spi *hs, u32 mode)
{
	writeb((mode & (SPI_CPHA | SPI_CPOL)) << 1, hs->iobase + R_SF_CPU_DMA_CTRL + 1);
}

static int hc_spi_setup(struct spi_device *spi)
{
	struct hc_spi  *hs = spi_master_get_devdata(spi->master);

	writeb(SFLASH_CONF3_DEFCONFIG, hs->iobase + R_SF_CONF3);
	writeb(SFLASH_CONF2_DEFCONFIG, hs->iobase + R_SF_CONF2);
	writeb(SFLASH_CONF1_DEFCONFIG, hs->iobase + R_SF_CONF1);
	writeb(SFLASH_CONF0_DEFCONFIG, hs->iobase + R_SF_CONF0);

	hc_spi_set_clock_mode(hs, spi->mode);

	return 0;
}

static void hc_spi_dma_transfer_tx(struct hc_spi *hs, void *buf, u32 len)
{
	struct device *dev = &hs->pdev->dev;
	u32 val;
	dma_addr_t tx_dma;

	tx_dma = dma_map_single(dev, buf, len, DMA_TO_DEVICE);
	if (dma_mapping_error(dev, tx_dma)) {
		dev_err(dev, "DMA map tx fail!\n");
		return;
	}

	init_completion(&hs->completion);

	writel(tx_dma & F_SF_0058_DMA_MEM_ADDR_M, hs->iobase + R_SF_DMA_MEM_ADDR);

	val = readl(hs->iobase + R_SF_CONF0);
	val |= (1 << 21 | 1 << 15);
	writel(val, hs->iobase + R_SF_CONF0);

	//writel(SFLASH_DMACTRL_TX_DEFCONFIG | hs->spi->chip_select, hs->iobase + R_SF_DMA_CTRL);
	writel(SFLASH_DMACTRL_TX_DEFCONFIG, hs->iobase + R_SF_DMA_CTRL);

	if (wait_for_completion_timeout(&hs->completion, DMA_TRANS_TIMEOUT) == 0) {
		dev_err(dev, "DMA tx timed out!\n");
		return;
	}

	val = readl(hs->iobase + R_SF_CONF0);
	val &= ~(1 << 21 | 1 << 15);
	writel(val, hs->iobase + R_SF_CONF0);

	dma_unmap_single(dev, tx_dma, len, DMA_TO_DEVICE);

	return;
}

static void hc_spi_dma_transfer_rx(struct hc_spi *hs, void *buf, u32 len)
{
	struct device *dev = &hs->pdev->dev;
	dma_addr_t rx_dma;
	u32 val;

	rx_dma = dma_map_single(dev, buf, len, DMA_FROM_DEVICE);
	if (dma_mapping_error(dev, rx_dma)) {
		dev_err(dev, "DMA map rx fail!\n");
		return;
	}

	init_completion(&hs->completion);

	writel(rx_dma & F_SF_0058_DMA_MEM_ADDR_M, hs->iobase + R_SF_DMA_MEM_ADDR);

	val = readl(hs->iobase + R_SF_CONF0);
	val |= (1 << 21 | 1 << 14);
	writel(val, hs->iobase + R_SF_CONF0);

	//writel(SFLASH_DMACTRL_RX_DEFCONFIG | hs->spi->chip_select, hs->iobase + R_SF_DMA_CTRL);
	writel(SFLASH_DMACTRL_RX_DEFCONFIG, hs->iobase + R_SF_DMA_CTRL);

	if (wait_for_completion_timeout(&hs->completion, DMA_TRANS_TIMEOUT) == 0) {
		dev_err(dev, "DMA rx timed out!\n");
		return;
	}

	val = readl(hs->iobase + R_SF_CONF0);
	val &= ~(1 << 21 | 1 << 14);
	writel(val, hs->iobase + R_SF_CONF0);

	dma_unmap_single(dev, rx_dma, len, DMA_FROM_DEVICE);

	return;
}

static void hc_spi_dma_transfer(struct hc_spi *hs, void *buf, u32 len, int dir)
{
	writel(len, hs->iobase + R_SF_SQI_COUNT);
	writel(0, hs->iobase + R_SF_DMA_FLASH_ADDR);
	writel(len, hs->iobase + R_SF_DMA_LEN);

	if (dir == SPI_DMA_TX) {
		hc_spi_dma_transfer_tx(hs, buf, len);
	} else {
		hc_spi_dma_transfer_rx(hs, buf, len);
	}

	return;
}

static void hc_spi_pio_rx(struct hc_spi *hs, u8 *buf, u32 len)
{
	u8 *dst = buf;
	u32 copy_left = len;
	u32 pioaddr = (u32)hs->piobase[hs->spi->chip_select];

	while (copy_left) {
		if (!IS_ALIGNED((u32)dst, 4) || copy_left < 4) {
			*dst++ = *(u8 *)pioaddr;
			copy_left--;
			continue;
		}
		if (copy_left >= 4) {
			*(u32 *)dst = *(u32 *)pioaddr;
			dst += 4;
			copy_left -= 4;
		}
	}

	return;
}

static void hc_spi_pio_tx(struct hc_spi *hs, u8 *buf, u32 len)
{
	u8 *src = buf;
	u32 copy_left = len;
	u32 pioaddr = (u32)hs->piobase[hs->spi->chip_select];

	while (copy_left) {
		*(u8 *)(pioaddr + *src) = *src;
		src++;
		copy_left--;
	}

	return;
}

static void hc_spi_set_multi_io(struct hc_spi *hs, int mode)
{
	u8 val;

	val = readb(hs->iobase + R_SF_CONF2);
	val &= ~(F_SF_009A_MODE_M);
	val |= mode;
	writeb(val, hs->iobase + R_SF_CONF2);
}

static void hc_spi_transfer_tx(struct hc_spi *hs, void *buf, unsigned len)
{
	u32 pioaddr;
	u32 copy_left;
	u32 dma_copy_left;
	u8 *src;

	writeb(F_SF_0099_DATA_HIT, hs->iobase + R_SF_CONF1);

	/*
	 * Read IO register to confirm the configuration before transfer, otherwise
	 * there might be cc error
	 */
	ioread32(hs->iobase);

	/*
	 * PIO mode always using cpu copy
	 */
	if (!hs->dma_mode || len < DMA_LEN_MIN) {
		hc_spi_pio_tx(hs, buf, len);
		return;
	}

	copy_left = len;
	src = (u8 *)buf;
	pioaddr = (u32)hs->piobase[hs->spi->chip_select];
	while (copy_left) {
		if (!IS_ALIGNED((u32)src, DMA_ADDR_ALIGN) ||
		    copy_left < DMA_LEN_ALIGN) {
			*(u8 *)(pioaddr + *src) = *src;
			src++;
			copy_left--;
			continue;
		}

		dma_copy_left = ALIGN_DOWN(copy_left, DMA_LEN_ALIGN);
		if (dma_copy_left) {
			hc_spi_dma_transfer(hs, src, dma_copy_left, SPI_DMA_TX);
			src += dma_copy_left;
			copy_left -= dma_copy_left;
		}
	}

	return;
}

static void hc_spi_transfer_rx(struct hc_spi *hs, void *buf, unsigned len)
{
	u32 pioaddr;
	u32 copy_left;
	u32 dma_copy_left;
	u8 *dst;

	writeb(F_SF_0099_DATA_HIT, hs->iobase + R_SF_CONF1);

	/*
	 * Read IO register to confirm the configuration before transfer, otherwise
	 * there might be cc error
	 */
	ioread32(hs->iobase);

	/*
	 * PIO mode always using cpu copy
	 */
	if (!hs->dma_mode || len < DMA_LEN_MIN) {
		hc_spi_pio_rx(hs, buf, len);
		return;
	}

	copy_left = len;
	dst = (u8 *)buf;
	pioaddr = (u32)hs->piobase[hs->spi->chip_select];
	while (copy_left) {
		if (!IS_ALIGNED((u32)dst, DMA_ADDR_ALIGN) ||
		    copy_left < DMA_LEN_ALIGN) {
			*dst++ = *(u8 *)pioaddr;
			copy_left--;
			continue;
		}

		dma_copy_left = ALIGN_DOWN(copy_left, DMA_LEN_ALIGN);
		if (dma_copy_left) {
			hc_spi_dma_transfer(hs, dst, dma_copy_left, SPI_DMA_RX);
			dst += dma_copy_left;
			copy_left -= dma_copy_left;
		}
	}

	return;
}

static void  hc_spi_transfer_setup_mode(struct hc_spi *hs, struct spi_transfer *t)
{
	unsigned int mode;

	mode = max_t(unsigned int, t->rx_nbits, t->tx_nbits);

	switch (mode) {
	case SPI_NBITS_QUAD:
		/*cmd + addr + data : 1 + 1 + 4*/
		hc_spi_set_multi_io(hs, SFLASH_MODE_5);
		break;
	case SPI_NBITS_DUAL:
		/*cmd + addr + data : 1 + 1 + 2*/
		hc_spi_set_multi_io(hs, SFLASH_MODE_1);
		break;
	default:
		/*cmd + addr + data : 1 + 1 + 1*/
		hc_spi_set_multi_io(hs, SFLASH_MODE_0);
		break;
	}
}

static void hc_spi_set_cs(struct spi_device *spi, bool enable)
{
	struct hc_spi *hs = spi_master_get_devdata(spi->master);
	u32 val;

	if (!enable) {
		//mutex_lock(&hs->lock);
		val = readl(hs->iobase + R_SF_TIMING_CTRL);
		val &= ~(F_SF_00C8_CS_PROGRAM_M | F_SF_00C8_CS_PROGRAM_EN_M);
		val |= ((3 << F_SF_00C8_CS_PROGRAM_S) | F_SF_00C8_CS_PROGRAM_EN);
		writel(val, hs->iobase + R_SF_TIMING_CTRL);
	} else {
		val = readl(hs->iobase + R_SF_TIMING_CTRL);
		val &= ~F_SF_00C8_CS_PROGRAM_M;
		writel(val, hs->iobase + R_SF_TIMING_CTRL);
		//mutex_unlock(&hs->lock);
	}
}

static int hc_spi_transfer_one(struct spi_master *master,
			       struct spi_device *spi,
			       struct spi_transfer *xfer)
{
	struct hc_spi *hs = spi_master_get_devdata(master);
	unsigned long speed_hz;
	static uint32_t *clk_reg_base;
	static uint32_t tmp_data;
	hs->spi = spi;

	speed_hz = spi->max_speed_hz;
	if (xfer->speed_hz)
		speed_hz = xfer->speed_hz;

	if(speed_hz > 54000000)
	{
		/* set spi main clk to 198Mhz */
		clk_reg_base = ioremap(CLK_SEL_REG1,CLK_SEL_REG1_LEN);
		if (clk_reg_base == NULL)
		{
			return -1;
		}
		tmp_data = readl(clk_reg_base);
		tmp_data |= 0x8000;
		writel(tmp_data,clk_reg_base);
		iounmap(clk_reg_base);		
	}
	
	if (speed_hz > hs->sclk)
		speed_hz = hs->sclk;

	hc_spi_set_clk_rate(hs, speed_hz);

	hc_spi_set_clock_mode(hs, spi->mode);

	hc_spi_transfer_setup_mode(hs, xfer);

	if (xfer->tx_buf != NULL && xfer->len != 0) {
		hc_spi_transfer_tx(hs, (void *)xfer->tx_buf, xfer->len);
	}

	if (xfer->rx_buf != NULL && xfer->len != 0) {
		hc_spi_transfer_rx(hs, xfer->rx_buf, xfer->len);
	}

	return 0;
}

static void hc_spi_cleanup(struct spi_device *spi)
{
	dev_dbg(&spi->dev, "cleanup!\n");
}

static irqreturn_t hc_spi_interrupt(int dummy, void *priv)
{
	struct hc_spi *hs = (struct hc_spi *)priv;

	writel(F_SF_00A0_DMA_LEN_ERR_ST | F_SF_00A0_DMA_INT_ST, hs->iobase + R_SF_INT_ST);
	writel(0, hs->iobase + R_SF_DMA_CTRL);

	complete(&hs->completion);

	return IRQ_HANDLED;
}

static ssize_t sf_protect(struct device *dev, struct device_attribute *attr, const char *buf, size_t n)
{
	if (sysfs_streq(buf, "lock")) {
		mutex_lock(&slock);
	} else if (sysfs_streq(buf, "unlock")) {
		mutex_unlock(&slock);
	}

	return n;
}


static DEVICE_ATTR(protect, 0644,  NULL, sf_protect);

static struct attribute *sf_attributes[] = {
	&dev_attr_protect.attr,
	NULL
};

static const struct attribute_group sf_attr_group = {
	.attrs = sf_attributes,
};

static int hc_spi_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct spi_master *master;
	struct resource *res;
	struct hc_spi *hs;
	int ret;
	int i;

	hc_clk_enable(SFLASH_CLK);

	master = spi_alloc_master(&pdev->dev, sizeof(*hs));
	if (!master) {
		return -ENOMEM;
	}

	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_TX_DUAL |
			    SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD;
	master->setup = hc_spi_setup;
	master->dev.of_node = pdev->dev.of_node;
	master->bus_num = pdev->id;
	master->transfer_one = hc_spi_transfer_one;
	master->set_cs = hc_spi_set_cs;
	master->cleanup = hc_spi_cleanup;

	platform_set_drvdata(pdev, master);
	hs = spi_master_get_devdata(master);
	hs->pdev = pdev;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res) {
		dev_err(dev, "not found <reg> of sfiobase!\n");
		ret = -ENXIO;
		goto err_put_master;
	}
	hs->iobase = ioremap(res->start, res->end - res->start + 1);
	if (!hs->iobase) {
		dev_err(dev, "fail ioremap <reg> of sfiobase!\n");
		ret = -EFAULT;
		goto err_put_master;
	}

	hs->pctl = devm_pinctrl_get(&hs->pdev->dev);
	if (IS_ERR(hs->pctl)) {
		ret = -ENXIO;
		goto err_put_master;
	}

	hs->active = pinctrl_lookup_state(hs->pctl, "active");
	if (IS_ERR(hs->active)) {
		devm_pinctrl_put(hs->pctl);
		ret = -ENXIO;
		goto err_put_master;
	}

	if (pinctrl_select_state(hs->pctl, hs->active)) {
		devm_pinctrl_put(hs->pctl);
		ret = -ENXIO;
		goto err_put_master;
	}

	ret = of_property_read_u32(pdev->dev.of_node, "num-cs", &hs->num_cs);
	if (ret < 0) {
		hs->num_cs = 0;
		dev_err(dev, "not found <num-cs>!\n");
		ret = -ENXIO;
		goto err_put_master;
	}
	master->num_chipselect = hs->num_cs;

	if (hs->num_cs < MAX_INLINE_NUM_CS) {
		hs->piobase = hs->inlinepiobase;
	} else {
		hs->piobase = devm_kzalloc(dev, sizeof(void *) * hs->num_cs,
					   GFP_KERNEL);
		if (!hs->piobase) {
			ret = -ENXIO;
			goto err_put_master;
		}
	}

	for (i = 0; i < hs->num_cs; i++) {
		res = platform_get_resource(pdev, IORESOURCE_MEM, 1 + i);
		if (!res) {
			dev_err(dev, "not found <reg> of piobase%d!\n", i);
			ret = -ENXIO;
			goto err_put_master;
		}
		hs->piobase[i] = ioremap(res->start, res->end - res->start + 1);
		if (!hs->piobase[i]) {
			dev_err(dev, "fail ioremap <reg> of piobase%d!\n", i);
			ret = -EFAULT;
			goto err_put_master;
		}
	}

	hs->irq = platform_get_irq(pdev, 0);
	if (hs->irq < 0) {
		dev_err(dev, "not found <interrupts>!\n");
		ret = -ENXIO;
		goto err_put_master;
	}
	ret = request_irq(hs->irq, hc_spi_interrupt, 0, dev_name(&pdev->dev),
			  hs);
	if (ret != 0) {
		dev_err(dev, "error request irq %d!\n", hs->irq);
		goto err_put_master;
	}

	ret = of_property_read_u32(pdev->dev.of_node, "sclk", &hs->sclk);
	if (ret < 0) {
		hs->sclk = 54000000;
	}

	ret = of_property_read_u32(pdev->dev.of_node, "dma-mode",
				   &hs->dma_mode);
	if (ret < 0) {
		/* Default dma mode */
		hs->dma_mode = 1;
	}
	hs->dma_mode = !!hs->dma_mode;

	/* Set default dma-mask to 32 bit. */
	dev->coherent_dma_mask = DMA_BIT_MASK(32);
	/* Set default coherent_dma_mask. */
	if (!dev->dma_mask)
		dev->dma_mask = &dev->coherent_dma_mask;

	init_completion(&hs->completion);
	mutex_init(&slock);

	ret = spi_register_master(master);
	if (ret) {
		dev_err(dev, "failed register spi master!\n");
		goto err_free_irq;
	}

	ret = sysfs_create_group(&dev->kobj, &sf_attr_group);

	return 0;

err_free_irq:
	free_irq(hs->irq, &pdev->dev);
err_put_master:
	spi_master_put(master);

	return ret;
}

static int hc_spi_remove(struct platform_device *pdev)
{
	struct spi_master *master = platform_get_drvdata(pdev);
	struct hc_spi *hs = spi_master_get_devdata(master);

	free_irq(hs->irq, &pdev->dev);
	spi_unregister_master(master);
	platform_device_unregister(pdev);

	return 0;
}

static const struct of_device_id hc_spi_dt_ids[] = {
	{
		.compatible = "hichip,hc16xx-spi-sf",
	},
	{},
};

MODULE_DEVICE_TABLE(of, hc_spi_dt_ids);

static struct platform_driver hc_spi_driver = {
	.driver =
		{
			.name = "hc_spi_sf",
			.of_match_table = hc_spi_dt_ids,
		},
	.probe = hc_spi_probe,
	.remove = hc_spi_remove,
};

module_platform_driver(hc_spi_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Banner Xia <banner.xia@hichiptech.com>");
MODULE_DESCRIPTION("HiChip SPI Flash Controller driver");
